package cn.ibizlab.core.oauth2.service.impl; /**
 * Generate code from /{{projectName}}-user/src/main/java/{{packageName}}/core/{{modules}}/service/impl/{{entities@SQL}}ServiceImpl.java.hbs
 */
// @SkipOverwrite

import cn.ibizlab.core.oauth2.domain.OauthClientDetails;
import cn.ibizlab.core.system.domain.SysDeploySystem;
import cn.ibizlab.core.system.service.SysDeploySystemService;
import cn.ibizlab.util.errors.BadRequestAlertException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static cn.ibizlab.core.oauth2.constants.OAuth2Constant.*;


/**
 * 实体[oauth客户端信息] 服务对象接口实现
 *
 * @author generator
 */
@Service("OauthClientDetailsService")
public class OauthClientDetailsServiceImpl extends AbstractOauthClientDetailsService implements ClientDetailsService {

    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    SysDeploySystemService deploySystemService;

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {

        //内置客户端（兼容老jwt密码模式登录）
        if(PREDEFINE_CLIENTDETAILS_ID.equals(clientId))
            return getPredefineClientDetails();

        //授权内置应用sso登录
        SysDeploySystem deploySystem = getDeploySystem(clientId);
        if(deploySystem != null){
            return getDeploySystemClientDetails(clientId);
        }

        OauthClientDetails oauthClient = getSelf().getById(clientId);
        if (oauthClient == null) {
            throw new UsernameNotFoundException("用户" + clientId + "未找到");
        }
        String clientSecret = oauthClient.getClientSecret();
        if(ObjectUtils.isEmpty(clientSecret)){
            throw new UsernameNotFoundException("获取" + clientId + "应用秘钥失败");
        }

        BaseClientDetails client = new BaseClientDetails();
        if(!ObjectUtils.isEmpty(oauthClient.getScope()))
            client.setScope(Arrays.asList(oauthClient.getScope().split(",")));
        if(!ObjectUtils.isEmpty(oauthClient.getAutoapprove()))
            client.setAutoApproveScopes(Arrays.asList(oauthClient.getAutoapprove().split(",")));
        client.setClientId(clientId);
        if(!ObjectUtils.isEmpty(oauthClient.getAuthorizedGrantTypes()))
            client.setAuthorizedGrantTypes(Arrays.asList(oauthClient.getAuthorizedGrantTypes().split(",")));
        client.setClientSecret(passwordEncoder.encode(clientSecret));
        if(!ObjectUtils.isEmpty(oauthClient.getWebServerRedirectUri())) {
            Set<String> registeredRedirectUri = new LinkedHashSet<>();
            for(String uri : oauthClient.getWebServerRedirectUri().split(",|;|\\n"))
                if(!ObjectUtils.isEmpty(uri.trim()))
                    registeredRedirectUri.add(uri);
            client.setRegisteredRedirectUri(registeredRedirectUri);
        }

        if(!ObjectUtils.isEmpty(oauthClient.getAuthorities())){
            List<GrantedAuthority> authorityList = new ArrayList<>();
            for(String strAuthority : oauthClient.getAuthorities().split(",|;|\\n"))
                if(!ObjectUtils.isEmpty(strAuthority.trim()))
                    authorityList.add(new SimpleGrantedAuthority(strAuthority.trim()));
            client.setAuthorities(authorityList);
        }
        //设置 accessToken、refreshToken有效期
        if(!ObjectUtils.isEmpty(oauthClient.getAccessTokenValidity()))
            client.setAccessTokenValiditySeconds(oauthClient.getAccessTokenValidity());
        else
            client.setAccessTokenValiditySeconds((int)(expiration/1000));
        if(!ObjectUtils.isEmpty(oauthClient.getRefreshTokenValidity()))
            client.setRefreshTokenValiditySeconds(oauthClient.getRefreshTokenValidity());

        return client;
    }

    @Value("${ibiz.jwt.expiration:7200000}")
    private Long expiration;


    private ClientDetails predefineClientDetails = null;
    protected ClientDetails getPredefineClientDetails() {
        if(predefineClientDetails == null)
            predefineClientDetails = createPredefineClientDetails(PREDEFINE_CLIENTDETAILS_ID);
        return predefineClientDetails;
    }

    /**
     * 内置客户端（兼容老jwt密码模式登录）
     * @param clientId
     * @return
     */
    protected ClientDetails createPredefineClientDetails(String clientId){
        BaseClientDetails client = new BaseClientDetails();
        client.setScope(Arrays.asList(PREDEFINE_CLIENTDETAILS_SCOPES));
        client.setClientId(clientId);
        client.setAuthorizedGrantTypes(Arrays.asList(PREDEFINE_CLIENTDETAILS_GRANTTYPE_PASSWORD,PREDEFINE_CLIENTDETAILS_GRANTTYPE_REFRESH_TOKEN));
        client.setClientSecret(passwordEncoder.encode(clientId));
        client.setAccessTokenValiditySeconds((int)(expiration/1000));
        return client;
    }

    /**
     * 获取内置系统客户端
     * @param clientId
     * @return
     */
    protected ClientDetails getDeploySystemClientDetails(String clientId) {
        BaseClientDetails client = new BaseClientDetails();
        client.setScope(Arrays.asList(PREDEFINE_CLIENTDETAILS_SCOPES));
        client.setClientId(clientId);
        client.setAuthorizedGrantTypes(Arrays.asList(PREDEFINE_CLIENTDETAILS_GRANTTYPE_PASSWORD,PREDEFINE_CLIENTDETAILS_GRANTTYPE_REFRESH_TOKEN,PREDEFINE_CLIENTDETAILS_GRANTTYPE_AUTHORIZATION_CODE));
        client.setClientSecret("");
        client.setAccessTokenValiditySeconds((int)(expiration/1000));
        client.setAutoApproveScopes(Arrays.asList("all"));
        String requestRedirectUri = getRedirectUri();
        if(!ObjectUtils.isEmpty(requestRedirectUri)) {
            Set<String> registeredRedirectUri = new LinkedHashSet<>();
            for(String uri : requestRedirectUri.split(",|;|\\n"))
                if(!ObjectUtils.isEmpty(uri.trim()))
                    registeredRedirectUri.add(uri);
            client.setRegisteredRedirectUri(registeredRedirectUri);
        }
        return client;
    }

    /**
     * 内置应用客户端
     * @param clientId
     * @return
     */
    protected SysDeploySystem getDeploySystem(String clientId) {
        return deploySystemService.getById(clientId);
    }

    /**
     * 获取重定向地址
     * @return
     */
    protected String getRedirectUri() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(requestAttributes!=null) {
            HttpServletRequest request = requestAttributes.getRequest();
            if (request != null && request.getParameter("redirect_uri") != null) {
                return  request.getParameter("redirect_uri");
            }
        }
        return null;
    }

    /**
     * 生成应用密钥
     * @param dto
     * @return
     */
    @Override
    public OauthClientDetails generateClientSecret(OauthClientDetails dto) {
        dto.setClientSecret(UUID.randomUUID().toString());
        return dto;
    }

    @Override
    @CacheEvict(value = "oauthclientdetails", key = "'row:'+#p0.clientId")
    public OauthClientDetails cacheEvict(OauthClientDetails dto) {
        return super.cacheEvict(dto);
    }
}